package serv

import (
	"d-serv-go/server/core/bean"
	"d-serv-go/server/core/consts"
	"d-serv-go/server/core/db"
	"d-serv-go/server/core/util"
	strutils "d-serv-go/server/core/util/stringutils"
	"errors"
	"github.com/kirinlabs/utils/str"
	"regexp"
	"strings"
)

var SqlBuilder = newSqlBuilder()

type sqlBuilder struct {
}

func (b *sqlBuilder) Insert(serv *ServBean, dataBean *bean.Bean, preValue []interface{}) (string, []interface{}) {
	initInsertData(serv, dataBean)
	bfField := strutils.NewStringBuilder("insert into ")
	bfField.Append(serv.GetTableAction()).Append(" (")
	bfValue := strutils.NewStringBuilder(") values (")
	items := serv.GetTableItems()
	for _, item := range items {
		itemCode := item.GetStr("ITEM_CODE")
		bfField.Append(itemCode).Append(",")
		bfValue.Append("?,")
		if !dataBean.Contains(itemCode) { //没有数据项则使用缺省值
			def := item.GetStr("ITEM_INPUT_DEFAULT")
			if (len(def) > 0) && strings.Index(def, "@") >= 0 {
				def = ServUtils.ReplaceSysVars(def)
			}
			dataBean.Set(itemCode, def)
		}
		if item.GetStr("ITEM_FIELD_TYPE") == (consts.ITEM_FIELD_TYPE_NUM) && dataBean.Contains(itemCode) { //处理数据类型
			if dataBean.IsNotEmpty(itemCode) {
				if strings.Index(item.GetStr("ITEM_FIELD_LENGTH"), consts.SEPARATOR) > 0 { //浮点
					preValue = append(preValue, dataBean.GetFloat32(itemCode))
				} else { //整形
					switch dataBean.Get(itemCode).(type) {
					case string:
						preValue = append(preValue, dataBean.GetStr(itemCode))
					case int, int8, int16, int32, int64:
						preValue = append(preValue, dataBean.GetInt(itemCode))
					default:
						preValue = append(preValue, dataBean.Get(itemCode))
					}
				}
			} else {
				preValue = append(preValue, nil)
			}
		} else if item.GetStr("ITEM_FIELD_TYPE") == consts.ITEM_FIELD_TYPE_DATE {
			if dataBean.IsEmpty(itemCode) {
				preValue = append(preValue, nil)
			} else {
				preValue = append(preValue, nil)
				date := util.DateUtil.GetDateFromString(dataBean.GetStr(itemCode))
				preValue = append(preValue, date)
			}
		} else {                           //其他类型
			if serv.GetStr("SAFE_FLAG") == consts.YES { //启用安全html，进行替换
				value := dataBean.GetStr(itemCode)
				value = strings.ReplaceAll(value, "<", "&lt;")
				value = strings.ReplaceAll(value, ">", "&gt;")
				preValue = append(preValue, value)
			} else {
				preValue = append(preValue, dataBean.Get(itemCode))
			}
		}
	}
	// 去掉逗号
	bfField.Truncate(bfField.Len() - 1)
	bfValue.Truncate(bfValue.Len() - 1)
	bfField = bfField.Append(bfValue.ToString()).Append(")")
	return bfField.ToString(), preValue
}

func initInsertData(serv *ServBean, dataBean *bean.Bean) {
	// servId = serv.GetId();
	//List<Bean> combineItems = serv.getCombineItems();
	//for (Bean item : combineItems) { //处理组合值字段
	//	String itemCode = item.getStr("ITEM_CODE");
	//	dataBean.set(itemCode, ServUtils.genCombineItem(servId, itemCode,
	//		item.getStr("ITEM_INPUT_CONFIG"), dataBean));
	//}
	if len(dataBean.GetId()) == 0 { //处理主键字段
		if len(dataBean.GetStr(serv.GetStr("SERV_KEYS"))) > 0 {
			dataBean.SetId(dataBean.GetStr(serv.GetStr("SERV_KEYS")))
		} else { // 根据Id判断是否自动赋UUID
			dataBean.Set(serv.GetStr("SERV_KEYS"), util.Lang.GetUUID())
			dataBean.SetId(dataBean.GetStr(serv.GetStr("SERV_KEYS")))
		}
	}
}

func newSqlBuilder() *sqlBuilder {
	return &sqlBuilder{}
}

func (b *sqlBuilder) Select(serv *ServBean, paramBean *db.ParamBean) (string, []interface{}) {
	sb := strutils.NewStringBuilder("select ")
	values := []interface{}{}
	//在查询语句Select之后加入指定关键字，如：Distinct
	if paramBean.IsNotEmpty(consts.SELECT_KEYWORDS) {
		sb.Append(" ").Append(paramBean.GetStr(consts.SELECT_KEYWORDS)).Append(" ")
	}

	if paramBean.Contains(consts.PARAM_SELECT) {
		keyField := serv.GetPKey()
		paramSelect := paramBean.GetStr(consts.PARAM_SELECT)
		reg, _ := regexp.Compile("\\s*((?i)" + keyField + "|distinct).*")
		//select为复杂结构（必然包含分组、子查询、distinct等不再处理主键字段）
		if paramBean.IsNotEmpty(consts.PARAM_GROUP) || strings.Index(paramSelect, "(") >= 0 || paramSelect == "*" || reg.Match([]byte(paramSelect)) {
			sb.Append(paramSelect)
		} else { //特殊处理主键字段，将主键字段放在第一个位置
			sb.Append(keyField).Append(",")
			if str.EndsWith(paramSelect, keyField) {
				sb.Append(strings.ReplaceAll(paramSelect, ",\\s*"+keyField, ""))
			} else {
				sb.Append(strings.ReplaceAll(
					paramSelect, keyField+",", ""))
			}
		}
	} else {
		items := serv.GetViewItems()
		for i, itemBean := range items {
			//获取全部字段数据
			sb.Append(itemBean.GetStr("ITEM_CODE"))
			if i < len(items)-1 {
				sb.Append(",")
			}
		}
	}
	var tableName string
	if paramBean.Contains(consts.PARAM_TABLE) {
		tableName = paramBean.GetStr(consts.PARAM_TABLE)
	} else {
		tableName = serv.GetTableView()
	}
	sb.Append(" from ").Append(tableName).Append(" where 1=1 ")
	if paramBean.Contains(consts.PARAM_WHERE) {
		if pw, ok := paramBean.Get(consts.PARAM_WHERE).(*strutils.StringBuilder);ok {
			sb.Append(pw.ToString())
		}
	} else {
		where, vals := b.preWhere(serv, paramBean)
		if len(vals) > 0 {
			values = append(values, vals...)
		}
		sb.Append(where)
	}
	if paramBean.IsNotEmpty(consts.PARAM_GROUP) {
		sb.Append(" group by ").Append(paramBean.GetStr(consts.PARAM_GROUP))
	}
	if paramBean.IsNotEmpty(consts.PARAM_ORDER) {
		sb.Append(" order by ").Append(paramBean.GetStr(consts.PARAM_ORDER))
	}
	return sb.ToString(), values
}

func (b *sqlBuilder) preWhere(serv *ServBean, dataBean *db.ParamBean) (string, []interface{}) {
	var values []interface{}
	sbSql := strutils.NewStringBuilder("")
	if len(dataBean.GetId()) > 0 { //设置了主键
		sbSql.Append(" and ").Append(serv.GetPKey()).Append("=?")
		values = append(values, dataBean.GetId())
	} else {
		for _, key := range dataBean.Keys() {
			itemBean := serv.GetItem(key)
			if itemBean != nil {
				if len(dataBean.GetStr(key)) > 0 { //处理非空
					sbSql.Append(" and ").Append(key).Append("=?")
					values = append(values, dataBean.Get(key))
				} else { //处理为空的情况
					sbSql.Append(" and ").Append(key).Append(" is null")
				}
			}
		}
	}
	return sbSql.ToString(), values
}

func (b *sqlBuilder) Where(servDef *ServBean, dataBean *bean.Bean) string {
	sbSql := strutils.NewStringBuilder()
	var quotes string
	if len(dataBean.GetId()) > 0 { //设置了主键
		itemBean := servDef.GetItem(servDef.GetPKey())
		if itemBean != nil {
			if itemBean.GetStr("ITEM_FIELD_TYPE") == consts.ITEM_FIELD_TYPE_NUM {
				quotes = ""
			} else {
				quotes = "'"
			}
			sbSql.Append(" and ").Append(servDef.GetPKey()).Append("=").Append(quotes)
			sbSql.Append(dataBean.GetId()).Append(quotes)
		}
	} else {
		for _, key := range dataBean.Keys() {
			itemBean := servDef.GetItem(key)
			if itemBean != nil {
				if itemBean.GetStr("ITEM_FIELD_TYPE") == consts.ITEM_FIELD_TYPE_NUM {
					quotes = ""
				} else {
					quotes = "'"
				}
				sbSql.Append(" and ").Append(key).Append("=").Append(quotes)
				sbSql.Append(dataBean.GetStr(key)).Append(quotes)
			}
		}
	}
	return sbSql.ToString()
}

func (b *sqlBuilder) Update(serv *ServBean, setBean *bean.Bean, whereBean *db.ParamBean, preValue []interface{}) (string, []interface{}, error) {
	values := []interface{}{}
	valuesLong := []interface{}{}
	sbSql := strutils.NewStringBuilder("update ").Append(serv.GetTableAction()).Append(" set ")
	sbLong := strutils.NewStringBuilder("")
	for _, key := range setBean.Keys() {
		item := serv.GetItem(key)
		if item != nil && item.GetStr("ITEM_FIELD_TYPE") == consts.ITEM_FIELD_TYPE_BIGTEXT { // 大文本往后排
			sbLong.Append(key).Append("=?,")
			valuesLong = append(valuesLong, setBean.Get(key))
		} else if (item != nil) && (item.GetInt("ITEM_TYPE") == consts.ITEM_TYPE_TABLE) {
			values = append(values, setBean.Get(key))
			sbSql.Append(key).Append("=?,")
		}
	}
	if serv.ContainsItem("S_MTIME") && setBean.IsEmpty("S_MTIME") { //存在未处理的更新时间
		sbSql.Append("S_MTIME=?,")
		values = append(values, util.DateUtil.GetDatetimeTS())
	}
	sbSql.Append(sbLong.ToString())
	values = append(values, valuesLong...)
	// 去掉逗号
	sbSql.Truncate(sbSql.Len() - 1)
	// 得到基于主键的where条件
	sbSql.Append(" where 1=1 ")
	var where string
	if whereBean.Contains(consts.PARAM_WHERE) {
		where = whereBean.GetStr(consts.PARAM_WHERE)
	} else {
		var vals []interface{}
		where, vals = b.preWhere(serv, whereBean)
		if len(vals) > 0 {
			values = append(values, vals...)
		}
	}
	if util.IsEmpty(where) {
		return "", nil, errors.New("NO WHERE UPDATE IS FORBIDDEN!")
	}
	sbSql.Append(where)
	preValue = append(values, preValue...)
	return sbSql.ToString(), preValue, nil
}
